package de.clapp.toolkit;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.ImageView;
import de.clapp.CLAppModel;
import de.clapp.Settings;
import de.clapp.backend.network.ClientRequest;
import de.clapp.backend.network.TCPSocket;
import de.clapp.backend.requests.BitmapRequest;
import de.clapp.data.Club;

public class ImageManager
{
    /** The INSTANCE. */
    private static ImageManager instance = new ImageManager();

    /** The cache. */
    private final Map<String, SoftReference<Bitmap>> cache;

    /** The pool. */
    private final ExecutorService pool;

    /** The image views. */
    private Map<ImageView, String> imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());

    /** The placeholder. */
    private Bitmap placeholder;

    private Activity activity;

    /**
     * Instantiates a new image manager.
     */
    private ImageManager()
    {
        this.cache = new HashMap<String, SoftReference<Bitmap>>();
        // this.pool = Executors.newFixedThreadPool(5);
        int cores = Runtime.getRuntime().availableProcessors();
        this.pool = new ThreadPoolExecutor(1, cores * 3, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    }

    public static ImageManager getInstance()
    {
        return ImageManager.instance;
    }

    /**
     * Sets the placeholder.
     * 
     * @param bmp
     *            the new placeholder
     */
    public void setPlaceholder(Bitmap bmp)
    {
        this.placeholder = bmp;
    }

    /**
     * Gets the bitmap from cache.
     * 
     * @param url
     *            the url
     * @return the bitmap from cache
     */
    public Bitmap getBitmapFromCache(String url)
    {
        if (cache.containsKey(url))
        {
            return cache.get(url).get();
        }
        else if (this.fileExists(url))
        {
            return this.loadBitmap(url);
        }
        else
        {
            return null;
        }
    }

    /**
     * Queue job.
     * 
     * @param url
     *            the url
     * @param imageView
     *            the image view
     * @param width
     *            the width
     * @param height
     *            the height
     */
    public void queueJob(final String url, final ImageView imageView, final int width, final int height, final int serverPort, final String serverIP)
    {
        // Create handler in UI thread
        final Handler handler = new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                String tag = imageViews.get(imageView);
                if (tag != null && tag.equals(url))
                {
                    if (msg.obj != null)
                    {
                        imageView.setImageBitmap((Bitmap) msg.obj);
                    }
                    else
                    {
                        imageView.setImageBitmap(placeholder);
                    }
                }
            }
        };

        pool.submit(new Runnable()
        {
            public void run()
            {
                final Bitmap bmp = downloadBitmap(url, width, height, serverPort, serverIP);
                Message message = Message.obtain();
                message.obj = bmp;

                handler.sendMessage(message);
            }
        });
    }

    /**
     * Load bitmap.
     * 
     * @param url
     *            the url
     * @param imageView
     *            the image view
     * @param width
     *            the width
     * @param height
     *            the height
     */
    public void loadBitmap(final String url, final ImageView imageView, final int width, final int height, final int serverPort, final String serverIP)
    {
        imageViews.put(imageView, url);
        // Falls das Bitmap bereits geladen wurde, so wurde vor dem Namen die
        // ClubID vorgesetzt
        Club club = CLAppModel.getInstance().getCurrentClub();
        Bitmap bitmap = getBitmapFromCache(club.getId_club() + url);

        // check in UI Thread, so no concurrency issues
        if (bitmap != null)
        {
            Log.d(null, "Item loaded from cache: " + url);
            imageView.setImageBitmap(bitmap);
        }
        else
        {
            imageView.setImageBitmap(placeholder);
            queueJob(url, imageView, width, height, serverPort, serverIP);
        }
    }

    /**
     * Download bitmap.
     * 
     * @param url
     *            the url
     * @param width
     *            the width
     * @param height
     *            the height
     * @return the bitmap
     */
    private Bitmap downloadBitmap(String url, int width, int height, final int serverPort, final String serverIP)
    {
        ClientRequest request = null;
        try
        {
            if(url.contains("null"))
            {
                return null;
            }
            
            request = new ClientRequest(new TCPSocket(serverIP, serverPort));
            BitmapRequest bmrequest = new BitmapRequest(url);
            request.request(bmrequest);

            byte[] data = bmrequest.getItem();
            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

            bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
            cache.put(url, new SoftReference<Bitmap>(bitmap));
            this.saveBitmap(bitmap, url);
            
            return bitmap;
        }
        catch (MalformedURLException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        catch (Exception e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally
        {
            try
            {
                request.close();
            }
            catch (Exception e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        return null;
    }

    public void setParentActivity(Activity activity)
    {
        this.activity = activity;
    }

    private void saveBitmap(Bitmap bitmap, String url)
    {
        try
        {
            url = url.replace("/", "");
            Club club = CLAppModel.getInstance().getCurrentClub();            
            BufferedOutputStream out = new BufferedOutputStream(activity.getBaseContext().openFileOutput(club.getId_club() + url, 0));
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
            out.flush();
            out.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private boolean fileExists(String name)
    {
        name = name.replace("/", "");
        File file = activity.getBaseContext().getFileStreamPath(name);
        return file.exists();
    }

    private Bitmap loadBitmap(String name)
    {
        try
        {
            name = name.replace("/", "");
            BufferedInputStream is = new BufferedInputStream(activity.getBaseContext().openFileInput(name));
            Bitmap bitmap = BitmapFactory.decodeStream(is);
            return bitmap;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
}
